﻿// ===============================================================================
// XCI.NET 西安交通信息WinForm开发框架 2018 版权所有
// ===============================================================================
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using XCI.Helper;

namespace XCI.Core
{
    /// <summary>
    /// 队列任务
    /// </summary>
    /// <typeparam name="T">队列对象</typeparam>
    public class QueueTask<T>
    {
        private System.Timers.Timer queueTimer;
        private BlockingCollection<T> addQueue;
        private BlockingCollection<List<T>> writeQueue;
        private List<T> cache;
        private readonly int queueSize = 1000000;
        private int index;
        private bool isInit;

        /// <summary>
        /// 队列名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 处理超时时间戳,默认:1分钟
        /// </summary>
        public TimeSpan Interval { get; set; } = TimeSpan.FromMinutes(1);

        /// <summary>
        /// 处理缓冲大小,默认:2000
        /// </summary>
        public int Buffer { get; set; } = 2000;

        /// <summary>
        /// 处理事件
        /// </summary>
        public event EventHandler<QueueProcessEventArgs<T>> Process;

        public QueueTask()
        {

        }

        public QueueTask(string name)
        {
            this.Name = name;
        }

        /// <summary>
        /// 初始化
        /// </summary>
        public void Init()
        {
            if (isInit) return;

            addQueue = new BlockingCollection<T>(queueSize);
            writeQueue = new BlockingCollection<List<T>>(queueSize / 2);
            cache = new List<T>(Buffer);

            InitQueueTimer();
            Task.Factory.StartNew(AddQueueProcess, TaskCreationOptions.LongRunning);
            Task.Factory.StartNew(WriteQueueProcess, TaskCreationOptions.LongRunning);
            isInit = true;
        }

        /// <summary>
        /// 添加对象到队列中
        /// </summary>
        /// <param name="obj">队列对象</param>
        public void Add(T obj)
        {
            addQueue.Add(obj);
            //DebugHelper.Debug($"队列:{Name}:数据加入到队列中");
        }

        /// <summary>
        /// 初始化缓存写入超时计时器
        /// </summary>
        private void InitQueueTimer()
        {
            queueTimer = new System.Timers.Timer();
            queueTimer.Interval = Interval.TotalMilliseconds;
            queueTimer.AutoReset = false;
            queueTimer.Elapsed += (o, e) =>
            {
                DebugHelper.DebugConsole($"队列:{Name}:缓存写入超时时间已到,创建新的缓存并把已满的缓存加入写队列中");
                AddCacheToWriteQueue();
            };
        }

        private void OnProcess(List<T> data, int index)
        {
            Process?.Invoke(this, new QueueProcessEventArgs<T>(Name, index, data));
        }

        /// <summary>
        /// 后台扫描队列处理
        /// </summary>
        private void AddQueueProcess()
        {
            foreach (var item in addQueue.GetConsumingEnumerable())
            {
                cache.Add(item);
                queueTimer.Start();
                //DebugHelper.Debug("消费队列,开启写入计时器,把对象加入缓存中");
                if (cache.Count == Buffer)
                {
                    DebugHelper.DebugConsole($"队列:{Name}:当前缓存已满,停止写入计时器,创建新的缓存并把已满的缓存加入写队列中");
                    queueTimer.Stop();
                    AddCacheToWriteQueue();
                }
            }
        }

        /// <summary>
        /// 写文件队列处理
        /// </summary>
        private void WriteQueueProcess()
        {
            foreach (var item in writeQueue.GetConsumingEnumerable())
            {
                index++;
                OnProcess(item, index);
            }
        }

        /// <summary>
        /// 缓存切换
        /// </summary>
        private void AddCacheToWriteQueue()
        {
            var oldCache = cache;
            cache = new List<T>(Buffer);
            writeQueue.Add(oldCache);
        }
    }

    /// <summary>
    /// 队列任务处理事件参数
    /// </summary>
    public class QueueProcessEventArgs<T> : EventArgs
    {
        public QueueProcessEventArgs(string name, int index, List<T> data)
        {
            Name = name;
            Index = index;
            Data = data;
        }

        public string Name { get; set; }

        /// <summary>
        /// 处理批次
        /// </summary>
        public int Index { get; set; }

        public List<T> Data { get; set; }
    }
}
